package parserjc.jaccie;

import java.util.*;

public abstract class EvaluatorParallel implements EvaluatorAction {

	//private TestFenster testFenster = new TestFenster();

    protected EvaluatorTree tree;

    private Vector attributes = new Vector();

    protected String activeRule;
    private String actSymbol;
    private boolean activeType = true;
    private EvaluatorTree actTreeNode;

	private Stack history = new Stack();

    private Stack values = new Stack();

    private boolean firstStep = true;

    /**
    * Erzeugt einen neuen Evaluator und setzt als Eingabe den Baum.
    */
    public EvaluatorParallel(EvaluatorTree tree) {
		this();
		this.setTree(tree);
    }

    /**
    * Erzeugt einen neuen Evaluator.
    */
    public EvaluatorParallel() {
        addAttributes();
    }

    //wird von der abgeleiteten Klasse implementiert
    protected abstract void addAttributes();

	protected void addAttribute(String name, String type) {
        Vector v = new Vector();
        v.add(name);
        v.add(type);
        attributes.add(v);
    }

    /**
    * Setzt den zu evaluierenden Syntaxbaum.
    */
    public void setTree(EvaluatorTree tree) {
        this.tree = tree;
        insertAttributes();
    }

    /**
    * Setzt den zu evaluierenden Syntaxbaum.
    */
	public void setTree(String stringTree) {
        StringTokenizer st = new StringTokenizer(stringTree,"\n");
		if (st.hasMoreTokens()) tree = new EvaluatorTree(st.nextToken().trim());
        EvaluatorTree helpTree = tree;
        int count = 0;
        while (st.hasMoreTokens()) {
            String next = st.nextToken();
            StringTokenizer st2 = new StringTokenizer(next);
            int count2 = 0;
            for (count2=0; next.charAt(count2) == ' '; count2++);
            if ((count2 == count+2) && (st2.hasMoreTokens())) {
                EvaluatorTree nextTree = new EvaluatorTree(st2.nextToken());
                helpTree.addSon(nextTree);
                if (st2.hasMoreTokens()) {
                    String attribute = next.substring(next.indexOf((char)34)+1,next.lastIndexOf((char)34));
                    Vector<String> attr = new Vector<String>();
                    attr.addElement("string");
                    attr.addElement("0");
                    attr.addElement("done");
                    attr.addElement(attribute);
                    nextTree.getAttributes().addElement(attr);
                }
				count = count2;
                helpTree = nextTree;
            }
            else if ((count2 == count) && (st2.hasMoreTokens())) {
                EvaluatorTree nextTree = new EvaluatorTree(st2.nextToken());
                helpTree = helpTree.getParent();
                helpTree.addSon(nextTree);
                if (st2.hasMoreTokens()) {
                    String attribute = next.substring(next.indexOf((char)34)+1,next.lastIndexOf((char)34));
                    Vector<String> attr = new Vector<String>();
                    attr.addElement("string");
                    attr.addElement("0");
                    attr.addElement("done");
                    attr.addElement(attribute);
                    nextTree.getAttributes().addElement(attr);
                }
				count = count2;
                helpTree = nextTree;
            }
            else if ((count2 < count) && (st2.hasMoreTokens())) {
                EvaluatorTree nextTree = new EvaluatorTree(st2.nextToken());
				for (int i=0; i <= count-count2; i=i+2)
	                helpTree = helpTree.getParent();
                helpTree.addSon(nextTree);
                if (st2.hasMoreTokens()) {
                    String attribute = next.substring(next.indexOf((char)34)+1,next.lastIndexOf((char)34));
                    Vector<String> attr = new Vector<String>();
                    attr.addElement("string");
                    attr.addElement("0");
                    attr.addElement("done");
                    attr.addElement(attribute);
                    nextTree.getAttributes().addElement(attr);
                }
				count = count2;
                helpTree = nextTree;
            }
        }
    }

	//wird von der abgeleiteten Klasse implementiert
    protected abstract void insertAttributes();

	protected void insertAttribute(EvaluatorTree tree, String nont, String attr) {
        if (tree.getName().equals(nont))
            tree.addAttribute(attr,getType(attr));
        for (int i=0; i < tree.getNumberOfSons(); i++)
            insertAttribute(tree.getSon(i),nont,attr);
    }

	/**
    * Liefert den Typen des Attributes,
    * falls es in den Attributen enthalten ist, sonst true.
    * @return true wenn synthetisiert
    * @return false wenn ererbt
    */
    private boolean getType(String attr) {
        for (int i=0; i < attributes.size(); i++)
            if (((String)((Vector)attributes.elementAt(i)).elementAt(0)).equals(
                attr))
                return (((String)((Vector)attributes.elementAt(i)).elementAt(1))
                	.equals("1"));
        return true;
    }

    /**
    * Liefert den Wert eines Attributes, falls es existiert und berechnet ist,
    * sonst null.
    */
	protected Object get(String attribute) {
        //Attributnamen besorgen
        String attr = attribute.substring(attribute.lastIndexOf(".")+1);
        //Knotennamen besorgen
        String node = attribute.substring(0,attribute.lastIndexOf("."));

        //Knotenposition innerhalb der Regel
        int i = -1;
        StringTokenizer st = new StringTokenizer(activeRule);
        int j = 0;
        while ((st.hasMoreTokens()) && (i == -1)) {
            if (st.nextToken().equals(node)) i=j;
            j++;
        }
		//Knoten holen - unterscheiden, ob synthetisiert oder ererbt
        EvaluatorTree t = null;
		if (activeType) { //synthetisiert
			if (i == 0) { //linke Seite
				t = actTreeNode;
            }
            else if (i > 0) {
                t = actTreeNode.getSon(i-1);
            }
        }
        else { //ererbt
        	if (i == 0) {
            	t = actTreeNode.getParent();
        	}
        	else if (i > 0) {
            	t = actTreeNode.getParent().getSon(i-1);
        	}
        }
		//ist das Attribute schon berechnet?
        Vector attrs = t.getAttributes();
        for (int k=0; k < attrs.size(); k++)
            if (((String)((Vector)attrs.elementAt(k)).elementAt(0)).equals(
            	attr)) {
                return ((Vector)attrs.elementAt(k)).elementAt(3);
        	}
		return null;
    }

    /**
    * Setzt den Wert eines Attributes.
    */
	protected void set(String attribute, Object toSet) {
        //Attributnamen besorgen
        String attr = attribute.substring(attribute.lastIndexOf(".")+1);
		//das Attribute suchen und eintragen.
        Vector attrs = actTreeNode.getAttributes();
        for (int k=0; k < attrs.size(); k++)
            if (((String)((Vector)attrs.elementAt(k)).elementAt(0)).equals(
            	attr)) {
            	((Vector)attrs.elementAt(k)).setElementAt("done",2);
            	((Vector)attrs.elementAt(k)).setElementAt(toSet,3);
        	}
    }

    /**
    * Implementation f&uuml;r EvaluatorAction-Interface.
    */
	public String nextStep() {
        if (nextStep(tree) == null) {
            if (!checkIfTreeIsReady(tree)) return "cycle";
            else return "complete";
        }
        else return "at work";
    }

    /**
    * F&uuml;hrt den Evaluator von aktueller Stelle bis zum Schluss aus.
    */
    public String doAll() {
        String returnStr = "at work";
        while (returnStr.equals("at work"))
            returnStr = nextStep();
        return returnStr;
    }

    /**
    * Setzt den Evaluator zur&uuml;ck.
    */
    public void reset() {
        history.clear();
        values.clear();
		insertAttributes();
        firstStep = true;
    }

	/**
    * Evaluiert genau ein berechenbares Attribut des Baumes.
	* Mit folgender Notation...
    *                Wurzel
    *              /   |   \
    *         Sohn0  Sohn1  Sohn2
    *        /    \           |
    *    Sohn0    Sohn1     Sohn0
    *      Attr0    Attr0     Attr0 <- "0.2.0:0"
    *               Attr1 <- "0.0.1:1"
    */
    public void evaluate(String address) {
        EvaluatorTree t = tree;
		StringTokenizer st = new StringTokenizer(
            address.substring(0,address.indexOf(":")),".");
        if (st.hasMoreTokens()) st.nextToken();
		while (st.hasMoreTokens())
            t = t.getSon(Integer.parseInt(st.nextToken()));
        int k = Integer.parseInt(address.substring(address.indexOf(":")+1));
		Vector attr = (Vector)t.getAttributes().elementAt(k);
        if ((attr.elementAt(2) != null) &&
            (attr.elementAt(2).equals("ready"))) //kann berechnet werden?
	        evaluate(new StackAttribute("",attr,"",t,k));
    }

    public void back() {
        if (history.isEmpty()) {
            if (!firstStep) reset();
	        //testFenster.update(history,0,values,values);
            return;
        }
		StackAttribute value = (StackAttribute)history.pop();

        value.setState("ready");
	    value.setValue(null);
        values.push(value);
        //testFenster.update(history,0,values,values);

		//was ist jetzt noch berechenbar?
        //(kann nur oben oder unten bzw. hier geschehen sein)
        EvaluatorTree helpTree = value.getTree();
		if (helpTree.getParent() != null) {
            //Vater
            checkNodeIfPossible(helpTree.getParent());
            //Br�der
	        for (int j=0; j < helpTree.getParent().getNumberOfSons(); j++)
	            checkNodeIfPossible(helpTree.getParent().getSon(j));
        }
        else checkNodeIfPossible(helpTree); //falls nicht bei den Br�dern selbst
        //S�hne
        for (int j=0; j < helpTree.getNumberOfSons(); j++)
            checkNodeIfPossible(helpTree.getSon(j));

        //testFenster.update(history,0,values,values);
    }

    private EvaluatorTree nextStep(EvaluatorTree t) {
        //erstmalig den Baum nach auswertbaren Attributen durchsuchen
		if (firstStep) {
            firstStep = false;
            //hier wird der Berbeitungskeller gef�llt (values)
            checkAllIfPossible(t);
            //testFenster.update(history,0,values,values);
            return t;
        }
		//gibt es noch etwas zu tun
        if (values.isEmpty()) return null;
        //nimm oberstes Element vom Bearbeitungskeller
        StackAttribute value = (StackAttribute)values.pop();
        //auf dem Bearbeitungskeller das erste nehmen, das berechnet werden kann
        while ((value.getState() == null) || (value.getState().equals("done"))){
	        if (values.isEmpty()) return null;
			value = (StackAttribute)values.pop();
        }
		//value ist berechenbar
        //testFenster.update(history,0,values,values);
		return evaluate(value);
    }

	private EvaluatorTree evaluate(StackAttribute value) {
        //Auswertungskontext zusammensuchen...
        //f�r synthetisierte Attribute
        EvaluatorTree t = value.getTree();
		if (value.getType().equals("1")) {
            activeType = true;
			activeRule = t.getName();
            for (int j=0; j < t.getNumberOfSons(); j++)
                activeRule = activeRule+" "+t.getSon(j).getName();
			activeRule = checkDoubleNames(activeRule);
            actTreeNode = t;
            int index = activeRule.indexOf(" ");
            if (index == -1) actSymbol = activeRule;
            else actSymbol = activeRule.substring(0,index);
        }
        //f�r ererbte Attribute
        else {
            activeType = false;
            activeRule = t.getParent().getName();
            int pos=0;
            for (int j=0; j < t.getParent().getNumberOfSons(); j++) {
                 activeRule = activeRule+" "+t.getParent().getSon(j).getName();
                 if (t.getParent().getSon(j) == t) pos=j;
            }
            //bei Doppelname erfolgt Nummerieren
			activeRule = checkDoubleNames(activeRule);
            //den jetzt g�ltigen Symbol-Namen raussuchen
            StringTokenizer st = new StringTokenizer(activeRule);
            for (int j=0; j < pos+2; j++)
                actSymbol = st.nextToken();
            //den aktuellen Knoten setzen
            actTreeNode = t;
        }
		//berechne jetzt den Wert des Attributes
		calculate(actSymbol+"."+value.getName());

        //merken, was ge�ndert wurde!
        history.push(value);
        //testFenster.update(history,0,values,values);

		//ist jetzt mehr berechenbar geworden?
        //(kann nur oben oder unten bzw. hier geschehen sein)
		if (t.getParent() != null) {
            checkNodeIfPossible(t.getParent());
	        for (int j=0; j < t.getParent().getNumberOfSons(); j++)
	        	checkNodeIfPossible(t.getParent().getSon(j));
        }
        else checkNodeIfPossible(t);
        for (int j=0; j < t.getNumberOfSons(); j++)
            checkNodeIfPossible(t.getSon(j));

        //dient hier der Information "noch nicht beendet"
        //testFenster.update(history,0,values,values);
		return t;
    }

	private void checkAllIfPossible(EvaluatorTree t) {
        checkNodeIfPossible(t);
	    for (int i=0; i < t.getNumberOfSons(); i++) {
            checkAllIfPossible(t.getSon(i));
        }
    }

	private void checkNodeIfPossible(EvaluatorTree t) {
		//geh alle Attribute des Knotens durch...
		for (int i=0; i < t.getAttributes().size(); i++) {
            Vector attr = (Vector)t.getAttributes().elementAt(i);
            if ((attr.elementAt(2) == null) ||
                (!attr.elementAt(2).equals("done"))) {
	            //Auswertungskontext zusammensuchen...
    	        //f�r synthetisierte Attribute
				if (attr.elementAt(1).equals("1")) {
            	    activeType = true;
					activeRule = t.getName();
	                for (int j=0; j < t.getNumberOfSons(); j++)
    	                activeRule = activeRule+" "+t.getSon(j).getName();
					activeRule = checkDoubleNames(activeRule);
            	    actTreeNode = t;
                    int index = activeRule.indexOf(" ");
                    if (index == -1) actSymbol = activeRule;
                	else actSymbol = activeRule.substring(0,index);
	            }
    	        //f�r ererbte Attribute
        	    else {
            	    activeType = false;
                	activeRule = t.getParent().getName();
	                int pos=0;
    	            for (int j=0; j < t.getParent().getNumberOfSons(); j++){
        	            activeRule = activeRule+" "+
            	          	t.getParent().getSon(j).getName();
	                    if (t.getParent().getSon(j) == t) pos=j;
    	            }
        	        //bei Doppelname erfolgt Nummerieren
					activeRule = checkDoubleNames(activeRule);
	                //den jetzt g�ltigen Symbol-Namen raussuchen
    	            StringTokenizer st = new StringTokenizer(activeRule);
        	        for (int j=0; j < pos+2; j++)
            	        actSymbol = st.nextToken();
                	//den aktuellen Knoten setzen
	                actTreeNode = t;
    	        }
        	    //Markiere die Knoten entsprechend
				if (checkIfPossible(actSymbol+"."+(String)attr.elementAt(0))) {
                    if (attr.elementAt(2) == null) {
		                attr.setElementAt("ready",2);
    	                //f�ge in den Bearbeitungskeller ein
        	            values.push(new StackAttribute(
            	            actSymbol+"."+(String)attr.elementAt(0),attr,
                            activeRule,actTreeNode,i));
                    }
                }
    	        else
        	        attr.setElementAt(null,2);
            }
        }
        //testFenster.update(history,0,values,values);
    }


	/**
    * F�gt bei doppelten Symbolnamen Nummerierung ein.
    * Folge -> Folge Ziffer    wird zu:   Folge1 -> Folge2 Ziffer
    */
	private String checkDoubleNames(String rule) {
        Vector help = new Vector();
        Vector symbols = new Vector();
        StringTokenizer st = new StringTokenizer(rule);
        while(st.hasMoreTokens()) {
            String symbol = st.nextToken();
            if (symbols.contains(symbol)) help.add(symbol);
            symbols.add(symbol);
        }
        for (int i=0; i < help.size(); i++) {
            int c = 1;
            for (int j=0; j < symbols.size(); j++) {
                if (symbols.elementAt(j).equals(help.elementAt(i))) {
                    symbols.setElementAt((String)help.elementAt(i)+c,j);
                    c++;
                }
            }
        }
        String returnString = "";
        for (int i=0; i < symbols.size(); i++) {
            returnString = returnString+(String)symbols.elementAt(i)+" ";
        }
        return returnString.substring(0,returnString.length()-1);
    }

	protected boolean check(String attribute) {
        //Attributnamen besorgen
        String attr = attribute.substring(attribute.lastIndexOf(".")+1);
        //Knotennamen besorgen
        String node = attribute.substring(0,attribute.lastIndexOf("."));

        //Knotenposition innerhalb der Regel
        int i = -1;
        StringTokenizer st = new StringTokenizer(activeRule);
        int j = 0;
        while ((st.hasMoreTokens()) && (i == -1)) {
            if (st.nextToken().equals(node)) i=j;
            j++;
        }
		//Knoten holen - unterscheiden, ob synthetisiert oder ererbt
        EvaluatorTree t = null;
		if (activeType) { //synthetisiert
			if (i == 0) { //linke Seite
				t = actTreeNode;
            }
            else if (i > 0) {
                t = actTreeNode.getSon(i-1);
            }
        }
        else { //ererbt
        	if (i == 0) {
            	t = actTreeNode.getParent();
        	}
        	else if (i > 0) {
            	t = actTreeNode.getParent().getSon(i-1);
        	}
        }
		//ist das Attribute schon berechnet?
        Vector attrs = t.getAttributes();
        for (int k=0; k < attrs.size(); k++)
            if (((String)((Vector)attrs.elementAt(k)).elementAt(0)).equals(
            	attr)) {
            	return ((((Vector)attrs.elementAt(k)).elementAt(2) != null) &&
                    (((Vector)attrs.elementAt(k)).elementAt(2).equals("done")));
        	}
        return false;
    }

	/**
    * Liefert true, wenn alle Attribute berechnet wurden.
    */
	private boolean checkIfTreeIsReady(EvaluatorTree t) {
        //Attribute von t pr�fen
        Vector attributes = t.getAttributes();
        for (int i=0; i < attributes.size(); i++) {
            Vector attribute = (Vector)attributes.elementAt(i);
            if ((attribute.elementAt(2) == null) ||
                (!attribute.elementAt(2).equals("done"))) return false;
        }
		//die S�hne ebenfalls pr�fen
		for (int i=0; i < t.getNumberOfSons(); i++) {
            if (!checkIfTreeIsReady(t.getSon(i))) return false;
        }
        //wenn keiner was zu beanstanden hatte, dann ist es ok
        return true;
    }

    //Die Implementation wird erzeugt.
    protected abstract boolean checkIfPossible(String attr);

    //Die Implementation wird erzeugt.
    protected abstract void calculate(String attr);

	/**
    * Gibt den Baum im derzeitigen Bearbeitungsstatus zur&uuml;ck.
    */
    public EvaluatorTree getTree() {return tree;}
    /**
    * Gibt die Attribute als Vectoren in einem Vector zur&uuml;ck.
    */
    public Vector getAttributes() {return attributes;}

	/**
    * F&uuml;hrt den Evaluator aus.
    */
    public String start() {
        insertAttributes();
        return doAll();
    }
    /**
    * Hat hier heine Bedeutung.
    * Liefert immer true (Hand-Modus ist immer eingeschalten).
    */
    public boolean switchHandMode() {return true;}
}
